package mse.tx.arch.pipe.systemb;

import java.io.IOException;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Properties;

import mse.tx.arch.pipe.framework.DataModel;
import mse.tx.arch.pipe.framework.FilterFramework;
import mse.tx.arch.pipe.framework.IConfigurable;

public class FileSinkFilter extends FilterFramework implements IConfigurable {

	// default value of file name	
	private String fileName = "OutputB.dat";
	
	// object is used to print a file
	private PrintWriter output = null;

	/**
	 * Constructor method which initializes normal input port
	 */
	
	public FileSinkFilter() {
		addInputPortByName("input");
	}
	
	/**
	 * Constructor method which initializes normal input port
	 * 
	 * @param name of file that this filter writes
	 */
	
	public FileSinkFilter(String fileName) {
		addInputPortByName("input");
		this.fileName = fileName;
	}

	public void run() {
		System.out.print("\n" + this.getName() + "::Sink Reading \n");

		//boolean isExtrapolatedPressure = false;
		int id = 0;
		long measurement = 0;
		List<DataModel> aFrame = new ArrayList<DataModel>();
		DataModel dataRead;
		
		try {
			output = new PrintWriter(fileName);
			output.printf("Time: \t Temperature (C): \t Altitude (m): \t Pressure (psi): \n");
			while (true) {
				
				id = readID();
				measurement = readMeasurement();
				
				if(id == 0 && !aFrame.isEmpty()){

					writeAFrame(aFrame);
					aFrame.clear();
				}
				dataRead = new DataModel();
				dataRead.setId(id);
				dataRead.setMeasurement(measurement);
				aFrame.add(dataRead);	
			}
		} catch (IOException e) {
			e.printStackTrace();
		} catch (EndOfStreamException e) {
			// e.printStackTrace();
		} finally {
			output.close();
			closePorts();
		}
	}

	private void writeAFrame(List<DataModel> aFrame) throws IOException, EndOfStreamException{
		Date timeStamp = new Date();//.getInstance();
		SimpleDateFormat timeStampFormat = new SimpleDateFormat("yyyy:DD:HH:mm:ss");

		String timeStr = "";

		double altitude = 0;
		double pressure = 0;
		double temperature = 0;
		boolean isExtrapolate = false;
		
		for(DataModel model : aFrame){
			int id = model.getId();
			long measurement = model.getMeasurement();			
			switch (id) {
			case 0:
				timeStamp.setTime(measurement);
				timeStr = timeStampFormat.format(timeStamp);
				break;
			case 2:
				altitude = Double.longBitsToDouble(measurement);
				break;
			case 3:
				pressure = Double.longBitsToDouble(measurement);
				//isExtrapolatedPressure = false;
				break;
			case 4:
				temperature = Double.longBitsToDouble(measurement);
				break;
			case DataModel.EXTAPLOTATED:
				isExtrapolate  = true;
				break;
			default:
				break;
			}

		}
		System.out.printf("%s\t", timeStr);
		output.printf("%s\t", timeStr);
		
		// write Temperature
		System.out.printf("%9.5f\t", temperature);
		output.printf("%9.5f\t", temperature);
		
		// write Altitude
		System.out.printf("%11.5f\t", altitude);
		output.printf("%11.5f\t", altitude);
		
		// write Pressure
		System.out.printf("%11.5f", pressure);
		output.printf("%11.5f", pressure);
			
		if(isExtrapolate){
			System.out.printf("*\n");
			output.printf("*\n");
		}else{
			System.out.printf("\n");
			output.printf("\n");			
		}
	}
	
	/**
	 *  
	 *  Private method which reads stream of measurement's ID and combine them as a variable
	 *  
	 *   @return a variable that contains 8 bytes of measurement's value
	 * */
	
	private int readID() throws IOException, EndOfStreamException {
		int IdLength = 4; // This is the length of IDs in the byte stream

		int id = 0;

		byte databyte;

		for (int i = 0; i < IdLength; i++) {
			databyte = readFilterInputPort("input");

			id = id | (databyte & 0xFF);

			if (i != IdLength - 1) {
				id = id << 8;
			}
		}
		return id;
	}

	/**
	 *  
	 *  Private method which reads stream of measurement value and combine them as a variable
	 *  
	 *   @return a variable that contains 8 bytes of measurement's value
	 * */
	
	private long readMeasurement() throws IOException, EndOfStreamException {
		int MeasurementLength = 8;

		long measurement = 0;

		byte databyte;

		for (int i = 0; i < MeasurementLength; i++) {
			databyte = readFilterInputPort("input");
			measurement = measurement | (databyte & 0xFF);
			if (i != MeasurementLength - 1) {
				measurement = measurement << 8;
			}
		}

		return measurement;
	}

	/**
	 * This method allows that filename can be set via property file
	 */
	@Override
	public void setConfiguration(Properties props) {
		if (props != null) {
			setFilename(props.getProperty("filename"));
		}
	}

	/**
	 * This method allow developers change file name
	 * 
	 * @param name of file that this filter read
	 */
	public void setFilename(String filename) {
		if (filename != null)
			this.fileName = filename;
	}

} // FilterTemplate